package net.lovesrc.support.service.email;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import com.zhouzhou.fastjava.BizException;
import com.zhouzhou.fastjava.util.ThreadKit;
import io.socket.client.IO;
import io.socket.client.Socket;
import okhttp3.OkHttpClient;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URISyntaxException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class Get365PwEmailService implements EmailService, AutoCloseable {

    private static final Logger logger = LoggerFactory.getLogger(Get365PwEmailService.class);

    private Cache<String, Email> cache = CacheBuilder
            .newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(512)
            .build();

    private BlockingQueue<String> emails = new ArrayBlockingQueue<>(100);

    private Socket socket;

    public Get365PwEmailService() {
        newSocket();
    }

    private void newSocket() {
        IO.Options options = new IO.Options();
        options.transports = new String[]{"polling"};
        options.transportOptions = Maps.newHashMap();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(10, TimeUnit.HOURS)
                .connectTimeout(10, TimeUnit.HOURS)
                .writeTimeout(10, TimeUnit.HOURS)
                .build();
        options.webSocketFactory = okHttpClient;
        options.callFactory = okHttpClient;
        try {
            socket = IO.socket("http://get365.pw", options);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        socket.on(Socket.EVENT_CONNECT, (v) -> {
            logger.info("connect" + JSON.toJSONString(v));
        });
        socket.on(Socket.EVENT_DISCONNECT, (v) -> {
            logger.info("disconnect");
        });
        socket.on("mail", objects -> {
            JSONObject emailJson = (JSONObject) objects[0];
            logger.info("Email JSONObject: {}", emailJson.toString());

            JSONObject fromJson = emailJson.getJSONArray("from").getJSONObject(0);
            String fromEmail = fromJson.getString("address");
            String fromName = fromJson.getString("name");
            String text = emailJson.getString("text");

            JSONObject toJson = emailJson.getJSONArray("to").getJSONObject(0);
            String toEmail = toJson.getString("address");
            String toName = toJson.getString("name");

            String subject = emailJson.getString("subject");

            Email email = Email.builder()
                    .fromEmail(fromEmail)
                    .fromName(fromName)
                    .toEmail(toEmail)
                    .toName(toName)
                    .subject(subject)
                    .text(text)
                    .build();
            cache.put(toEmail, email);
        });
        socket.on("shortid", (mails) -> {
            String mail = mails[0].toString();
            emails.offer(mail + "@get365.pw");
        });
        socket.on(Socket.EVENT_ERROR, (e) -> {
            System.out.println(JSON.toJSONString(e));
        });
        socket.connect();
    }

    @Override
    public synchronized String newEmail() {
        socket.emit("request shortid", true);
        try {
            return emails.poll(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public synchronized Email getEmail(String toEmail) {
        for (int i = 0; i < 300; i++) {
            Email email = cache.getIfPresent(toEmail);
            if (email != null) {
                return email;
            }
            ThreadKit.sleepMilliseconds(200);
        }
        throw BizException.messageOf("未能获取到邮件");
    }

    @Override
    public void close() throws Exception {
        socket.disconnect();
        socket.close();
    }
}
